import pickle
import random
import sys

import pygame
from pygame.locals import *

from map import Map
from menu import Menu, Checkpoint
from resource import Img, Music
from reward import Reward
from tank import Tank, Superhero
from wall import Wall


class TankGame(object):
    """
    主类
    """

    def __init__(self):
        # 获取py game
        self.game = pygame
        # 设置游戏窗口宽高
        self.size = self.width, self.height = 960, 640
        # 定义坦克类型，0为敌人坦克，1、2为英雄坦克
        self.enemy, self.hero1, self.hero2 = 0, 1, 2
        # 定义1P、2P坦克生命数量
        self.hero1_num, self.hero2_num = 5, 5
        # 定义难度
        self.difficult = 0
        # 设置关卡
        self.checkpoint = 0
        # 编辑地图关卡
        self.edit_checkpoint = 0
        # 初始化game
        self.game.init()
        # 初始化声音
        self.game.mixer.init()
        # 生成游戏窗口
        self.screen = self.game.display.set_mode(self.size)
        # 设置窗口标题
        self.game.display.set_caption('坦克世界')
        # 载入背景音乐
        self.game.mixer.music.load('music/music.wav')
        # 设置音乐音量
        self.game.mixer.music.set_volume(0.5)
        # 载入图片，音效
        self.images = Img()
        self.music = Music()
        # 设置窗口图标
        self.game.display.set_icon(self.images.icon)
        # 定义地图数据
        self.map_data = None
        # 定义是否播放背景音乐
        self.is_music = True
        # 定义是否播放音效
        self.is_mixer = True
        # 定义是否运行状态
        self.is_run = False
        # 定义是否游戏结束
        self.is_over = False
        # 定义是否显示菜单
        self.is_show_menu = True
        # 定义是否显示编辑关卡
        self.is_show_checkpoint = False
        # 定义是否编辑地图
        self.is_edit_map = False
        # 实例化编辑地图
        self.map = Map(self)
        # 定义是否胜利
        self.is_win = False
        # 定义是否显示胜利提示
        self.is_show_win = True
        # 定义是否显示游戏结束画面
        self.is_show_over = True
        # 定义编辑地图时的选择框
        self.select_rect = None
        # 辅助游戏胜利画面定时退出
        self.step = 4000
        # 定义随机物品出现的间隔
        self.step_reward = 2000
        # 载入字体，并设置字号
        # self.font50 = self.game.font.Font('font/STXINGKA.ttf', 50)
        # self.font20 = self.game.font.Font('font/SIMLI.ttf', 18)
        # 设置字体是否加粗
        # self.font50.set_bold(True)
        # 实例化一个帧率
        self.clock = self.game.time.Clock()
        self.load_map()
        self.init()

    # 游戏初始化
    def init(self):
        self.init_args()
        self.init_wall()
        self.init_tank()
        self.init_menu()
        self.running()

    # 初始化各种参数
    def init_args(self):
        # 设置敌方坦克总数量，通过游戏难度和关卡不断增加
        self.enemy_num = 20 + (5 + self.difficult) * self.checkpoint
        # 设置短方坦克同时出现的数量，通过游戏难度和关卡不断增加
        self.enemy_show_num = 3 + self.difficult + self.checkpoint
        # 设置坦克速度，通过游戏难度和关卡不断增加
        self.tankspeed = round(2 + (self.checkpoint + 2 + self.difficult) / 10)
        # 我方正在游戏人数
        self.hero_num = 2
        # 初始化子弹、坦克、爆炸、墙、菜单、关卡、随机物品的各个集合
        self.missiles = self.game.sprite.Group()
        self.tanks = self.game.sprite.Group()
        self.bombs = self.game.sprite.Group()
        self.walls = self.game.sprite.Group()
        self.menus = self.game.sprite.Group()
        self.maps = self.game.sprite.Group()
        self.checkpoints = self.game.sprite.Group()
        self.reward = self.game.sprite.Group()

    # 读取map.pkl文件，导入地图数据
    def load_map(self):
        with open('map.pkl', 'rb')as f:
            self.map_data = pickle.load(f)

    # 初始化墙
    def init_wall(self):
        # 定义总部坐标
        self.home = (
        (0, 440, 580), (0, 440, 600), (0, 440, 620), (0, 460, 580), (0, 480, 580), (0, 500, 580), (0, 500, 600),
        (0, 500, 620), (4, 460, 600))
        for wall in self.map_data[self.checkpoint].get('data'):
            # 初始化类型小于5的所有墙，类型为5是用于编辑地图的空白墙
            if wall[0] < 5:
                self.walls.add(Wall(self, wall))
        for wall in self.home:
            # 初始化总部
            self.walls.add(Wall(self, wall))

    def init_map(self):
        # 初始化准备编辑的地图
        for item in self.map_data[self.edit_checkpoint].get('data'):
            self.maps.add(Wall(self, item))

    def init_tank(self):
        # 初始化我方英雄坦克
        # her1 = Tank(400, 600, self.hero1, self)
        # her1.is_super = True
        # self.reward.add(Superhero(her1,self))
        # self.tanks.add(her1)
        self.tanks.add(Tank(400, 600, self.hero1, self))
        self.tanks.add(Tank(520, 600, self.hero2, self))

    # 初始化菜单、关卡选择界面
    def init_menu(self):
        for i in range(4):
            # 初始化菜单
            self.menus.add(Menu(i, 362, 222 + 69 * i, self))
        # 初始化音乐、音效按钮
        self.menus.add(Menu(4, 820, 0, self))
        self.menus.add(Menu(5, 892, 0, self))
        # 初始化关卡界面
        pos = (610, 335), (709, 335), (610, 378), (709, 378), (610, 421), (709, 421)
        for index, item in enumerate(pos):
            self.checkpoints.add(Checkpoint(index, item[0], item[1], self))

    # 随时检测敌方坦克数量，实时增加
    def create_enemy_tank(self):
        # print(f'敌人总数量1：{self.enemy_num}')
        # if self.enemy_num > self.enemy_show_num - 1 and len(self.tanks) < self.enemy_show_num + self.hero_num:
        if self.enemy_num > 0 and len(self.tanks) < self.enemy_show_num + self.hero_num:
            # 在顶行位置生成一辆临时坦克，与所有坦克进行碰撞检测
            enemy_temp_tank = Tank(random.randrange(self.width - 39), 0, self.enemy, self)
            # print(f'敌人总数量2：{self.enemy_num}')
            # print(f'敌人正在运行数量：{self.enemy_show_num}')

            collide_list = self.game.sprite.spritecollide(enemy_temp_tank, self.tanks, False, self.game.sprite.collide_mask)
            # print(f'坦克组中的坦克数量1：{len(self.tanks)}')
            # print(f'碰撞列表：{collide_list}')
            if not collide_list:
                # 如果没有产生碰撞，加入到坦克队列，否则等下一帧再实行重新生成
                self.tanks.add(enemy_temp_tank)
                self.enemy_num -= 1
            # print(f'坦克组中的坦克数量2：{len(self.tanks)}')
            # print('-' * 20)
    # 进入编辑地图状态
    def edit_map(self):
        # 如果被编辑的地图为空，初始化地图
        if len(self.maps) == 0:
            self.init_map()
        # 更新墙跟随鼠标的位置
        self.map.cursor()
        # 画出所有墙
        self.maps.draw(self.screen)

    # 游戏运行状态
    # 参数：key 所有键盘按键的状态
    #      event 键盘按键松开状态
    def run(self, key, event):
        # 判断是否允许播放背景音乐，并且没有进行播放，就启动播放
        if self.is_music and not self.game.mixer.music.get_busy():
            self.game.mixer.music.play()
        # 传入按键状态和键盘松开事件，更新英雄坦克位置和发射子弹
        self.tanks.update(key, event)
        # 更新子弹
        self.missiles.update()
        # 更新爆炸
        self.bombs.update()
        # 画出墙
        self.walls.draw(self.screen)
        # 画出子弹
        self.missiles.draw(self.screen)
        # 画出坦克
        self.tanks.draw(self.screen)
        # 画出爆炸
        self.bombs.draw(self.screen)
        # 更新随机物品
        self.reward.update()
        # 画出随机物品
        self.reward.draw(self.screen)
        # 用星星画出英雄1号坦克的剩余数量
        self.screen.blit(self.images.hero_empty, (5, self.height - 20))
        # subsurface方法用于截取图片的某个部分
        self.screen.blit(self.images.hero_filled.subsurface(0, 0, 69 - 14 * (5 - self.hero1_num), 13),
                         (5, self.height - 20))
        # 用星星画出英雄2号坦克的剩余数量
        self.screen.blit(self.images.hero_empty, (self.width - 74, self.height - 20))
        self.screen.blit(self.images.hero_filled.subsurface(0, 0, 69 - 14 * (5 - self.hero2_num), 13),
                         (self.width - 74, self.height - 20))
        # 用数字画出敌方坦克的剩余数量
        enemy_now_num = self.enemy_num + len(self.tanks) - self.hero_num
        if enemy_now_num // 10 != 0:
            self.screen.blit(self.images.num.subsurface(enemy_now_num // 10 * 20, 0, 20, 17), (460, 2))
        self.screen.blit(self.images.num.subsurface(enemy_now_num % 10 * 20, 0, 20, 17), (480, 2))
        # self.screen.blit(self.font20.render('%s' % self.enemy_num, True, (0, 139, 0)), (470, 1))
        # self.screen.blit(self.font20.render('%s' % self.tankspeed, True, (0, 139, 0)), (520, 1))
        # self.screen.blit(self.font20.render('1P-Speed:%s' % self.hero1_obj.speed, True, (0, 139, 0)), (540, 1))

    # 显示菜单状态
    def show_menu(self, event):
        # 画出菜单背景
        self.screen.blit(self.images.menu_background, (0, 0))
        # 根据游戏是否运行的状态，指定菜单按钮是否禁用
        if self.is_run:
            show_menu = 1, 3, 4, 5
        else:
            show_menu = 0, 2, 3, 4, 5
        # 更新菜单
        self.menus.update(show_menu, event)
        # 画出菜单
        self.menus.draw(self.screen)

    # 显示关卡状态
    def show_checkpoint(self, event):
        # 画出关卡选择背景
        self.screen.blit(self.images.checkpoint_background, (596, 320))
        # 更新关卡选择按钮
        self.checkpoints.update(event)
        # 画出按钮
        self.checkpoints.draw(self.screen)

    # 显示游戏结束界面状态
    def over(self):
        if self.is_show_over:
            # 如果允许播放音效，播放游戏结束音效
            if self.is_mixer:
                self.music.over.play()
            # 停止游戏背景音乐
            self.game.mixer.music.stop()
            self.is_show_over = False
        # 计算游戏结束图片位置并画出
        over_rect = self.images.over.get_rect()
        over_rect.center = self.width / 2, self.height / 2
        self.screen.blit(self.images.over, over_rect)

    # 通过一关，显示胜利界面状态
    def win(self):
        if self.is_show_win:
            # 判断is_show_win控制播放音效和停止背景音乐
            self.game.mixer.music.stop()
            self.is_show_win = False
            if self.is_mixer:
                self.music.win.play()
        if self.step < 10:
            # 通过判断不断递减的时间小于10，则重置时间，并停止显示游戏胜利界面
            self.step = 4000
            self.is_win = False
            self.is_show_win = True
        elif self.step < 1500:
            # 通过判断不断递减的时间小于1500，则画出关数
            self.screen.blit(self.images.checkpoint[self.checkpoint][0], (430, 237))
            # pos = self.width / 2 - 88, self.height / 2 - 100
            # self.screen.blit(
            #     self.font50.render('第 %s 关' % (self.map_data[self.checkpoint].get('id')), True, (246, 61, 0)), pos)
            # name_pos = self.width / 2 - 100, self.height / 2 - 30
            # self.screen.blit(
            #     self.font20.render('本关地图作者：%s' % (self.map_data[self.checkpoint].get('name')), True, (246, 61, 0)),
            #     name_pos)

            # 继续递减时间
            self.step -= 5
        else:
            # 时间大于1500，画出胜利画面
            win_rect = self.images.win.get_rect()
            win_rect.center = self.width / 2, self.height / 2
            self.screen.blit(self.images.win, win_rect)
            self.step -= 5

    # 退出游戏
    def quit(self):
        self.game.quit()
        sys.exit()

    # 游戏开始
    def running(self):
        while True:
            # 定义键盘松开事件
            key_up_event = None
            # 定义鼠标事件
            mouse_event = None
            # 获取所有事件并循环判断
            for event in self.game.event.get():
                # 如果关闭游戏窗口，游戏退出
                if event.type == QUIT:
                    self.quit()
                # 如果键盘松开事件发生，传递到预先定义的变量中，准备传入到坦克中
                elif event.type == KEYUP:
                    key_up_event = event
                elif event.type == KEYDOWN:
                    # 如果键盘按下
                    if self.is_run and event.key == K_ESCAPE:
                        # 如果游戏正在进行，交按下Esc键，修改显示菜单状态
                        self.is_show_menu = not self.is_show_menu
                elif event.type == MOUSEBUTTONDOWN and self.is_show_menu:
                    # 如果鼠标按下
                    mouse_event = event

                if self.is_edit_map:
                    # 如果为编辑地图状态，把事件传到地图类中
                    self.map.update(event)

            # 获取所有键盘按键的状态，按下就为真
            key = self.game.key.get_pressed()
            # 填充背景
            self.screen.blit(self.images.background, (0, 0))

            if self.is_show_menu:
                # 显示菜单为真，调用显示菜单方法，并传入鼠标事件
                self.show_menu(mouse_event)
            elif self.is_edit_map:
                # 编辑地图为真，调用编辑地图方法
                self.edit_map()
            else:
                if self.is_over:
                    # 游戏结束为真，调用游戏结束方法
                    self.over()
                elif self.is_win:
                    # 游戏通关，调用胜利方法显示胜利界面
                    self.win()
                else:
                    # 否则调用游戏运行，并传入键盘状态和键盘松开事件
                    self.run(key, key_up_event)
            if self.is_show_checkpoint:
                # 如果显示关卡为真，调用显示关卡方法，并传入鼠标事件
                self.show_checkpoint(mouse_event)
            # 如果英雄1和英雄2数量为0，游戏结束
            if self.hero1_num == 0 and self.hero2_num == 0:
                self.is_over = True
            # 如果敌方坦克数量小于0，并且我方坦克大于0，顺利通关
            if self.enemy_num + len(self.tanks) - self.hero_num <= 0 and self.hero1_num + self.hero2_num > 0:
                if self.checkpoint < len(self.map_data) - 1:
                    # 如果关卡小于关卡总数量，关卡+1
                    self.checkpoint += 1
                    self.step_reward = 2000
                else:
                    # 否则，关卡从0再来，并难度+1
                    self.checkpoint = 0
                    self.difficult += 1
                # 显示游戏胜利界面
                self.is_win = True
                # 重置游戏数据，进入下一关
                self.init()
            if self.is_win:
                # 如果显示游戏胜利为真，调用游戏胜利方法
                self.win()
            if self.hero1_num + self.hero2_num < 9 and self.is_run and not self.is_show_menu:
                # 如果英雄坦克数量总和小于9，开始计时生成随机奖励
                if self.step_reward < 0:
                    # 计时完成生成随机物品
                    self.reward.add(Reward(self))
                    # 重置计时时间
                    self.step_reward = 2000
                    if self.is_music:
                        # 如果允许播放音效，生成随机物品时播放音效
                        self.music.new_reward.play()
                else:
                    # 否则，继续计时
                    self.step_reward -= 1
            if self.select_rect is not None:
                # 协助地图类画出实时选择框
                self.game.draw.rect(self.screen, (255, 255, 255), self.select_rect, 1)
            # 实时生成敌方坦克
            self.create_enemy_tank()
            # 设置游戏帧率
            self.clock.tick(35)
            # 更新整个画面
            self.game.display.update()